/*
*  Copyright 2019-2020 Zheng Jie
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*  http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/
package me.zhengjie.modules.gis.service.impl;

import cn.hutool.core.util.ArrayUtil;
import com.alibaba.fastjson.JSONArray;
import me.zhengjie.modules.gis.domain.*;
import me.zhengjie.modules.gis.service.dto.*;
import me.zhengjie.utils.*;
import lombok.RequiredArgsConstructor;
import me.zhengjie.modules.gis.repository.TransportationInHistoryRepository;
import me.zhengjie.modules.gis.service.TransportationInHistoryService;
import me.zhengjie.modules.gis.service.mapstruct.TransportationInHistoryMapper;
import me.zhengjie.utils.map.LonlatConver;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Timestamp;
import java.util.*;
import java.io.IOException;
import javax.persistence.criteria.Predicate;
import javax.servlet.http.HttpServletResponse;

/**
* @website https://el-admin.vip
* @description 服务实现
* @author ZhuangBing Yan
* @date 2022-09-07
**/
@Service
@RequiredArgsConstructor
public class TransportationInHistoryServiceImpl implements TransportationInHistoryService {

    private final TransportationInHistoryRepository transportationInHistoryRepository;
    private final TransportationInHistoryMapper transportationInHistoryMapper;

    @Override
    public Map<String,Object> queryAll(TransportationInHistoryQueryCriteria criteria, Pageable pageable){
        Page<TransportationInHistory> page = transportationInHistoryRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable);
        return PageUtil.toPage(page.map(transportationInHistoryMapper::toDto));
    }

    @Override
    public List<TransportationInHistoryDto> queryAll(TransportationInHistoryQueryCriteria criteria){
        return transportationInHistoryMapper.toDto(transportationInHistoryRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder)));
    }

    @Override
    @Transactional
    public TransportationInHistoryDto findById(Long id) {
        TransportationInHistory transportationInHistory = transportationInHistoryRepository.findById(id).orElseGet(TransportationInHistory::new);
        ValidationUtil.isNull(transportationInHistory.getId(),"TransportationInHistory","id",id);
        return transportationInHistoryMapper.toDto(transportationInHistory);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public TransportationInHistoryDto create(TransportationInHistory resources) {
        return transportationInHistoryMapper.toDto(transportationInHistoryRepository.save(resources));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(TransportationInHistory resources) {
        TransportationInHistory transportationInHistory = transportationInHistoryRepository.findById(resources.getId()).orElseGet(TransportationInHistory::new);
        ValidationUtil.isNull( transportationInHistory.getId(),"TransportationInHistory","id",resources.getId());
        transportationInHistory.copy(resources);
        transportationInHistoryRepository.save(transportationInHistory);
    }

    @Override
    public void deleteAll(Long[] ids) {
        for (Long id : ids) {
            transportationInHistoryRepository.deleteById(id);
        }
    }

    @Override
    public void download(List<TransportationInHistoryDto> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (TransportationInHistoryDto transportationInHistory : all) {
            Map<String,Object> map = new LinkedHashMap<>();
            map.put("车牌号", transportationInHistory.getCarNumber());
            map.put("卡号", transportationInHistory.getCardNumber());
            map.put("货物名称", transportationInHistory.getGoodName());
            map.put("发货单位", transportationInHistory.getDeliverCompany());
            map.put("区域", transportationInHistory.getArea());
            map.put("运输单位", transportationInHistory.getFeedCompany());
            map.put("处置单位", transportationInHistory.getDisposeCompany());
            map.put("进厂时间", transportationInHistory.getEnterInTime());
            map.put("毛重（吨）", transportationInHistory.getGrossWeight());
            map.put("皮重(吨)", transportationInHistory.getTareWeight());
            map.put("扣减(吨)", transportationInHistory.getDeductionWeight());
            map.put("净重(吨)", transportationInHistory.getNetWeight());
            map.put("出单人(创建人)", transportationInHistory.getCreateBy());
            map.put("车间名称", transportationInHistory.getWorkShopName());
            map.put("数据类型()", transportationInHistory.getType());
            map.put("数据状态", transportationInHistory.getStatus());
            map.put("是否删除( 0 未删除  1 已删除)", transportationInHistory.getIsDel());
            map.put("创建时间", transportationInHistory.getCreateTime());
            map.put("修改人", transportationInHistory.getUpdateBy());
            map.put("修改时间", transportationInHistory.getUpdateTime());
            map.put("起点经度坐标", transportationInHistory.getPositionStart());
            map.put("路经线坐标", transportationInHistory.getLinePosition());
            map.put("终点经度坐标", transportationInHistory.getPositionEnd());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }

    @Override
    public ReceivingDto getGisReceiving(Integer year) {
        if (year == null) {
            year = Calendar.getInstance().get(Calendar.YEAR);
        }
        String start = year + "-01-01 00:00:00";
        String end = (year+1) + "-01-01 00:00:00";
        List<TransportationInHistory> outHistoryList = transportationInHistoryRepository.findAll((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> res = new ArrayList<>();
            res.add(criteriaBuilder.between(root.get("enterInTime"), new Timestamp(DateUtil.parseDate(start, DateUtil.FORMAT_YYYY_MM_DD_HH_MM_SS).getTime()),
                    new Timestamp(DateUtil.parseDate(end, DateUtil.FORMAT_YYYY_MM_DD_HH_MM_SS).getTime())));
            return criteriaQuery.where(res.toArray(new Predicate[0])).getRestriction();
        });
        //创建结果对象
        ReceivingDto result = new ReceivingDto();
        result.setYear(year);
        if (outHistoryList.isEmpty()) {
            return result;
        }
        //月度统计
        HashMap<Integer, BigDecimal> monthlyReceiveMap = new HashMap<>();
        for (int i = 1; i <= 12; i++) {
            monthlyReceiveMap.put(i, BigDecimal.ZERO);
        }
        //违规企业统计
        HashMap<String, ReceivingData> violateCompanyMap = new HashMap<>();
        HashSet<String> companySet = new HashSet<>();
        HashSet<String> carNoSet = new HashSet<>();

        //具体地点的排放量统计
        HashMap<String, LineDto> locationEmissionMap = new HashMap<>();

        //遍历数据
        for (TransportationInHistory inHistory: outHistoryList) {
            if (inHistory.getIsDel()) {
                continue;
            }
            String feedCompany = inHistory.getFeedCompany();//运输公司
            if (Objects.equals(1, inHistory.getStatus()) && StringUtils.isNotBlank(feedCompany)) {
                ReceivingData violateData = violateCompanyMap.get(feedCompany);
                if (violateData == null) {
                    violateData = new ReceivingData();
                    violateData.setType(2);
                    violateData.setViolateCount(0);
                    violateData.setCompanyName(feedCompany);
                }
                violateData.setViolateCount(violateData.getViolateCount() + 1);
                violateCompanyMap.put(feedCompany, violateData);
                continue;
            }

            BigDecimal emit = inHistory.getNetWeight();
            if (emit == null || emit.equals(BigDecimal.ZERO) || inHistory.getEnterInTime() == null) {
                continue;
            }
            if (StringUtils.isNotBlank(inHistory.getCarNumber())) {
                carNoSet.add(inHistory.getCarNumber());
            }
            Timestamp timestamp = inHistory.getEnterInTime();
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(timestamp);
            int month = calendar.get(Calendar.MONTH);
            BigDecimal mon = monthlyReceiveMap.get(month);
            if (mon != null) {
                monthlyReceiveMap.put(month, mon.add(emit));
            }
            String deliverCompany = inHistory.getDeliverCompany();//发货地址
            if (StringUtils.isNotBlank(deliverCompany)) {
                companySet.add(deliverCompany);
            }
            if (StringUtils.isNotBlank(deliverCompany) && StringUtils.isNotBlank(inHistory.getLinePosition())) {
                LineDto data = locationEmissionMap.get(deliverCompany);
                if (data == null) {
                    data = new LineDto();
                    if (StringUtils.isNotBlank(inHistory.getPositionStart())) {
                        String[] splits = inHistory.getPositionStart().split(",");
                        double[] centers = LonlatConver.gcj02_To_Wgs84(Double.parseDouble(splits[0]), Double.parseDouble(splits[1]));
                        data.setPosition(centers[0] + "," + centers[1]);
                    }
                    List<BigDecimal[]> linePoints = JSONArray.parseArray(inHistory.getLinePosition(), BigDecimal[].class);
                    linePoints.forEach(ArrayUtil::reverse);
                    data.setLine(linePoints);
                    data.setSumWeight(BigDecimal.ZERO);
                    data.setDistrict(inHistory.getArea());
                    data.setType(false);//true/1为运出， 0/false运进
                }
                List<LineContentDto> contents = data.getContent();
                if (contents == null) {
                    contents = new ArrayList<>();
                }
                LineContentDto lineContentDto = new LineContentDto();
                lineContentDto.setEnterTime(timestamp);
                lineContentDto.setWeight(emit);
                lineContentDto.setStatus(false);
                contents.add(lineContentDto);
                data.setContent(contents);

                data.setSumWeight(data.getSumWeight().add(emit));
                locationEmissionMap.put(deliverCompany, data);
            }
        }

        //按月度收运统计
        List<ReceivingData> monthlyReceiveList = new ArrayList<>();
        BigDecimal totalReceive = BigDecimal.ZERO;
        for (Map.Entry<Integer, BigDecimal> entry: monthlyReceiveMap.entrySet()) {
            ReceivingData data = new ReceivingData();
            data.setType(1);
            data.setMonth(String.valueOf(entry.getKey()));
            data.setReceive(entry.getValue());
            totalReceive = totalReceive.add(entry.getValue());
            monthlyReceiveList.add(data);
        }
        result.setMonthlyReceive(monthlyReceiveList);
        result.setTotalReceive(totalReceive);
        result.setDailyReceive(totalReceive.divide(DateUtil.isLeafYear(year) ? new BigDecimal("366") : new BigDecimal("365"),2, RoundingMode.CEILING));

        //统计违规企业
        List<ReceivingData> violateList = new ArrayList<>(violateCompanyMap.values());
        if (!violateList.isEmpty()) {
            violateList.sort((o1, o2) -> o2.getViolateCount() - o1.getViolateCount());
        }
        result.setViolateCompanies(violateList);

        //按地点排放统计
        result.setLocationReceive(new ArrayList<>(locationEmissionMap.values()));

        result.setCarCount(carNoSet.size() == 0 ? 1 : carNoSet.size());
        result.setCompanyCount(companySet.size() == 0 ? 1: companySet.size());

        return result;
    }
}